package com.sky.controller.admin;

import com.sky.constant.JwtClaimsConstant;
import com.sky.dto.EmployeeDTO;
import com.sky.dto.EmployeeLoginDTO;
import com.sky.dto.EmployeePageQueryDTO;
import com.sky.entity.Employee;
import com.sky.properties.JwtProperties;
import com.sky.result.PageResult;
import com.sky.result.Result;
import com.sky.service.EmployeeService;
import com.sky.utils.JwtUtil;
import com.sky.vo.EmployeeLoginVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

/**
 * 员工管理
 */
@RestController     //在Spring Boot中，@RestController注解是一个用于创建RESTful Web服务的重要工具
@RequestMapping("/admin/employee")
@Slf4j
@Api(tags = "员工相关接口")
public class EmployeeController {

    @Autowired
    private EmployeeService employeeService;
    @Autowired
    private JwtProperties jwtProperties;

    /**
     * 登录
     *
     * @param employeeLoginDTO
     * @return
     */
    @PostMapping("/login")              //这里传进来了DTO，这里封装的是前端给我们提交过来的账号和密码    //DTO,数据传输对象，通常用于程序中各层之间传递数据
    @ApiOperation(value = "员工登录")
    //我们自定义了一个Result对象，用来返回的
    public Result<EmployeeLoginVO> login(@RequestBody EmployeeLoginDTO employeeLoginDTO) {
        log.info("员工登录：{}", employeeLoginDTO);//一个日志打印输出，用来帮助开发者了解程序运行情况，或在发生问题时提供足够的上下文信息以便于调试
        //crt+alt+点击longin，直接进入实现类方法
        Employee employee = employeeService.login(employeeLoginDTO);

        //登录成功后，生成jwt令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put(JwtClaimsConstant.EMP_ID, employee.getId());
        String token = JwtUtil.createJWT(   //使用了JwtUtil工具类，createJWT：就是创建了一个token
                jwtProperties.getAdminSecretKey(),
                jwtProperties.getAdminTtl(),
                claims);
//上面令牌token里面放的是啥 ？
//1.其实主体的数据是放在了map对象claims里面，而map对象里边我们其实放的是一个员工的id，也就是claims.put，claims.put括号里前面JwtClaimsConstant.EMP_ID其实是个字符串，这个字符串定义在常量类JwtClaimsConstant里，这个字符串具体值就是empid，而put的value通过employee.getId()获取它的主键值，然后放进去，最后token就生成出来
//2.这里注意看createJWT括号，生成token时传进来的几个参数，第一个参数getAdminSecretKey，其实就是个key，第二个getAdminTtl就是令牌的过期时间，而在这里并没有写定，是通过jwtProperties对象传进来的，jwtProperties对象封装了一些数据，那jwtProperties又是什么东西？
//  jwtProperties,其实就是我们的配置属性类，这个类在common里properties包里，这类中有个注解@ConfigurationProperties，是是springboot里边的，这个注解的作用表示我们当前这个类是一个配置属性类，这个类的作用就是来封装我们配置文件当中的一些配置项，它封装的是哪个配置文件当中的配置项？
//  它封装的是哪个配置文件当中的配置项？它封装的是我们springboot的配置文件。我们springboot配置文件在sky-server中resource下的两个application文件，首先加载主体的application.yml文件，因为application.yml里spring:profiles:active:dev，所以application-dev.yml也会生效。
//  那它找的是配置文件中哪个配置项呢？它找的是application.yml配置文件的sky.jwt这个配置项（jwtProperties里注解@ConfigurationProperties括号里边有写），配置文件的sky.jwt里边就是我们一些具体的配置项，这些具体的配置项对应着jwtProperties类中的属性
//3.所以jwtProperties类中三个属性分别封装着application.yml的sky.jwt三个配置项，所以说我们通过配置属性类这种方式，把我们springboot配置文件里边的一些配置项封装成一个Java对象，然后把这个Java对象注入给我们的这个controller，通过@Autowired这个注解注入进来
//  注进来后，在这个controller的token中，我们就能看到了getAdminSecretKey和getAdminTtl的密钥key和过期时间

        //令牌生成后，把我们的数据和令牌封装一下，响应给我们的前端页面，那封装什么数据呢？
        //这里其实我们封装成VO，EmployeeLoginVO主要是给我们前端页面来使用的
        //这里的封装是通过builder，也就是构建器来封装的，大家以前都是通过new一个对象，然后调用set方法来封装。
        //而我们这个地方是通过builder，然后调用这个属性，其实就是个方法，只不过这个方法名和我们的属性名是一致的，通过这种方式也可以构建一个java对象。但是前提是，这个EmployeeLoginVO对象里边的上面要加一个@Builder注解，加完注解后，就可以通过这方式构建对象了，最后调用一下.build方法把对象构建好
        EmployeeLoginVO employeeLoginVO = EmployeeLoginVO.builder()
                .id(employee.getId())
                .userName(employee.getUsername())
                .name(employee.getName())
                .token(token)
                .build();

        //最后把我们后端的结果再进行一个封装，封装到Result对象里
        return Result.success(employeeLoginVO);
    }
//也就是说，我们后端给前端响应的数据统一都是使用这个result

    /**
     * 退出
     *
     * @return
     */
    @PostMapping("/logout")
    @ApiOperation("员工退出")   //value可以省略
    public Result<String> logout() {
        return Result.success();
    }

    /**
     * 新增员工
     * @param employeeDTO
     * @return
     */
    @PostMapping    //2.因为是post方式请求，所以要加上post，因为类上已经有路径/admin/employee，所以这里不用再加路径
    @ApiOperation("新增员工")
    //1.新增员工返回值统一用result对象来封装     注意，返回的是json格式，需要使用@RequestBody
    public Result save(@RequestBody EmployeeDTO employeeDTO) {
        log.info("新增员工：{}", employeeDTO);   //{}其实是一个占位符，程序运行时会动态的把后边的参数拼上去
        System.out.println("当前线程的id：" + Thread.currentThread().getId());    //在控制台输出线程id，因为客户端发送的每次请求，后端的Tomcat服务器都会分配一个单独的线程来处理请求，通过线程id来获取登录用户的id
        employeeService.save(employeeDTO);      //新增方法，alt+回车，选择creat，可以直接在service里面扩展或创建该方法
        return Result.success();
    }

    /**
     * 员工分页查询
     * @param employeePageQueryDTO
     * @return
     */
    @GetMapping("/page")    //接口设计是get请求，所以使用GetMapping注解
    @ApiOperation("员工分页查询")
    public Result<PageResult> page(EmployeePageQueryDTO employeePageQueryDTO){  //根据接口设计，前端提交过来的数据是Query格式，而不是json，参数前不需要加注解@RequestBody，所以直接声明参数就可以
        log.info("员工分页查询，参数为：{}", employeePageQueryDTO);
        PageResult pageResult = employeeService.pageQuery(employeePageQueryDTO);    //我们希望employeeService的pageQuery方法返回PageResult对象，因为所有分页查询都要返回PageResult对象
        return Result.success(pageResult);  //最后把分页查询对象pageResult，封装成Result返回给前端
    }

    /**
     * 启用禁用员工账号
     * @param status
     * @param id
     * @return
     */
    @PostMapping("status/{status}")
    @ApiOperation("启用禁用员工账号")
    //1.这里方法名前的Result<~>，泛型<~>可写可不写，这个项目的规则，针对于这种查询类的操作，因为要返回Result里的data数据，建议把泛型加上；对于非查询类的，方法定义的public Result<~>的泛型可以不写，所以这里方法不是查询的，所以泛型可以不写
    public Result startOrStop(@PathVariable Integer status,Long id){    //2.声明参数接受前端发过来的请求，由接口原型设计可以知道传进来两个参数，一个是路径参数{status}，第二个是地址栏传的id；status是整型的,id是Long型，因为实体类全是Long型的；路径参数前面要加一个注解@PathVariable，地址栏不需要加注解，只要保证参数id和地址栏参数id名一致就可以
                                //3.如果路径参数和方法定义的路径参数不一样，还需要在参数注解后加路径参数的名，即@PathVariable("status") Inte...,如果一致可以省略
        log.info("启用禁用员工账号：{},{}",status,id);
        employeeService.startOrStop(status,id);
        return Result.success();
    }

    /**
     * 根据id查询员工信息
     * @param id
     * @return
     */
    @GetMapping("/{id}")
    @ApiOperation("根据id查询员工信息")
    //方法名前的Result<~>泛型需要指定一下，因为我们这个是查询性的操作，需要明确一下前端提交过来的数据具体是一个什么样的形式。通过看接口文档，可以知道data是一个Employee实体形式
    public Result<Employee> getById(@PathVariable Long id){   //想要获取动态参数的值，参数可以是long id，并加上路径参数注解@PathVariable
        Employee employee = employeeService.getById(id);    //调用Service方法，由泛型形式可以知道，我们想要返回Employee实体对象，所以这里也要返回实体对象
        return Result.success(employee);
    }

    /**
     * 编辑员工信息
     * @param employeeDTO
     * @return
     */
    @PutMapping
    @ApiOperation("编辑员工信息")
    //这里不是查询性操作，方法名前的Result<~>泛型可以不指定
    public Result update(@RequestBody EmployeeDTO employeeDTO){     //这里参数为啥用dto？其实可以用实体类对象，但根据接口文档Body，发现和新增员工一样，提交过来的数据项都是dto类型的，所以用dto
        log.info("编辑员工信息：{}", employeeDTO);
        employeeService.update(employeeDTO);
        return Result.success();
    }

}
